#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
重构后的教室查询工具
使用模块化设计的教室查询功能
"""

import datetime
import re
from typing import Type, Optional

from langchain.tools import BaseTool
from langchain_core.callbacks import CallbackManagerForToolRun
from pydantic import BaseModel, Field

# 导入自定义模块
from .building_mapper import BuildingMapper
from .cache_manager import CacheManager
from .classroom_client import ClassroomClient
from .result_formatter import ResultFormatter
from .room_analyzer import RoomAnalyzer


class ClassroomQueryInput(BaseModel):
    """教室查询工具的输入参数"""
    building_name: str = Field(description="教学楼名称，如：九教、思源、思西、逸夫等")
    week: Optional[int] = Field(default=None, description="周次，如: 7。不提供则使用当前周")
    day_of_week: Optional[str] = Field(default=None, description="星期几，1-7。不提供则使用今天")
    section: Optional[str] = Field(default=None, description="节次，1-7。不提供则查询全天")
    room_number: str = Field(description="可选：具体教室编号，如SY407、201等",
                             default="")


class ClassroomQueryTool(BaseTool):
    """重构后的教室查询工具"""

    name: str = "classroom_query"
    description: str = """查询北京交通大学教室的空闲情况或特定教室占用情况。
    
参数：
- building_name: 教学楼名称（如：九教、思源、思西）
- week/day_of_week/section: 可选，不提供则使用当前时间
- room_number: 可选，查询特定教室
"""
    args_schema: Type[ClassroomQueryInput] = ClassroomQueryInput
    return_direct: bool = True

    def __init__(self, session=None):
        # 调用父类初始化
        super().__init__()

        # 直接在 __dict__ 中设置属性，完全绕过 Pydantic
        self.__dict__['session'] = session
        self.__dict__['cache_manager'] = CacheManager()
        self.__dict__['client'] = ClassroomClient(session) if session else None

    def _normalize_building_name(self, building_name: str) -> str:
        """将教学楼名称映射为 building_code"""
        name = building_name.lower().strip()

        # 使用正则匹配，支持各种别名和变体
        if re.search(r'思.*西|sx|思源西', name):
            return "2"
        elif re.search(r'思.*东|sd|思源东', name):
            return "3"
        elif re.search(r'思源(?!.*[东西])|思源楼|sy(?!\d)', name):
            return "1"
        elif re.search(r'九教|9教|第九', name):
            return "4"
        elif re.search(r'八教|8教|第八', name):
            return "5"
        elif re.search(r'五教|5教|第五', name):
            return "6"
        elif re.search(r'二教|2教|第二', name):
            return "7"
        elif re.search(r'东.*一|东区一|东一', name):
            return "8"
        elif re.search(r'东.*二|东区二|东二', name):
            return "9"
        elif re.search(r'东.*三|东区三|东三', name):
            return "10"
        elif re.search(r'逸夫|yf', name):
            return "11"
        elif re.search(r'机械|jx', name):
            return "12"
        elif re.search(r'十七|17', name):
            return "13"
        elif re.search(r'科技|kj', name):
            return "103"
        elif re.search(r'知行|zx', name):
            return "107"
        else:
            if name.isdigit():
                return name
            return building_name

    def _get_current_time_info(self):
        """获取当前时间信息，返回 (week, day_of_week, section)"""
        now = datetime.datetime.now()

        # 计算周次（基于第8周 = 2025-11-03）
        week8_start_date = datetime.date(2025, 11, 3)
        current_date = now.date()
        days_diff = (current_date - week8_start_date).days
        week = 8 + (days_diff // 7)

        # 星期几（1-7）
        day_of_week = str(now.weekday() + 1)

        # 节次（根据当前时间）
        hour = now.hour
        if 8 <= hour < 10:
            section = "1"
        elif 10 <= hour < 12:
            section = "2"
        elif 12 <= hour < 14:
            section = "3"
        elif 14 <= hour < 16:
            section = "4"
        elif 16 <= hour < 18:
            section = "5"
        elif 19 <= hour < 21:
            section = "6"
        elif 21 <= hour < 22:
            section = "7"
        else:
            section = "1,2,3,4,5,6,7"

        return week, day_of_week, section

    def _run(self, building_name: str, week: Optional[int] = None, day_of_week: Optional[str] = None,
             section: Optional[str] = None, room_number: str = "",
             run_manager: Optional[CallbackManagerForToolRun] = None) -> str:
        """执行教室查询"""
        try:
            # 1. 如果时间参数为空，自动获取当前时间
            if week is None or day_of_week is None or section is None:
                auto_week, auto_day, auto_section = self._get_current_time_info()
                week = week or auto_week
                day_of_week = day_of_week or auto_day
                section = section or auto_section

            # 2. 将 building_name 映射为 building_code
            building_code = self._normalize_building_name(building_name)

            # 3. 标准化输入参数
            building_code_processed = BuildingMapper.normalize_building_code(building_code)
            selected_weekdays = self._parse_weekdays(day_of_week)
            selected_sections = self._parse_sections(section)

            # 4. 尝试从缓存获取数据
            cache_result = self._try_get_from_cache(
                building_code_processed, week, day_of_week, section, room_number, selected_weekdays, selected_sections
            )
            if cache_result:
                return cache_result

            # 5. 发起网络查询
            if not self.client:
                return "未登录系统，请先登录。"

            query_params = self._build_query_params(building_code_processed, week)
            html_content = self.client.query_classroom(query_params)

            # 6. 解析HTML内容
            rooms_info = self.client.parse_html_content(html_content)

            # 7. 分析教室可用性
            analysis_result = RoomAnalyzer.analyze_room_availability(
                rooms_info, selected_weekdays, selected_sections, room_number
            )

            # 8. 智能处理未找到教室的情况
            if room_number and not analysis_result['target_room_info']:
                # 尝试智能匹配相似教室
                suggested_room = RoomAnalyzer.find_similar_room(room_number, analysis_result['all_rooms'])
                if suggested_room:
                    # print(f"[DEBUG] 未找到精确匹配教室 {room_number}，但找到相似教室 {suggested_room}，自动查询")
                    # 使用建议的教室重新查询
                    analysis_result = RoomAnalyzer.analyze_room_availability(
                        rooms_info, selected_weekdays, selected_sections, suggested_room
                    )
                    # 修改结果说明
                    original_room = room_number
                    room_number = suggested_room

            # 9. 格式化结果
            result = self._format_result(
                analysis_result, building_code_processed, room_number, selected_weekdays, selected_sections
            )

            # 10. 如果是智能匹配的结果，添加说明
            if 'original_room' in locals() and original_room != room_number:
                result = f"未找到 {original_room}，已查询 {room_number}:\n{result}"

            # 11. 更新缓存（始终缓存教学楼级别的完整数据）
            if result and "未找到教室表格" not in result:
                self._update_cache(
                    building_code_processed, week, day_of_week, section,
                    rooms_info, analysis_result['all_rooms']
                )

            return result

        except Exception as e:
            error_msg = f"查询过程中发生错误: {str(e)}"
            print(f"[ERROR] {error_msg}")
            import traceback
            traceback.print_exc()
            return error_msg

    def _parse_weekdays(self, day_of_week: str) -> list:
        """解析星期几"""
        try:
            selected_weekdays = [int(x.strip()) for x in day_of_week.split(',')]
            selected_weekdays = [x for x in selected_weekdays if 1 <= x <= 7]
            if not selected_weekdays:
                selected_weekdays = list(range(1, 8))  # 默认所有星期
            return selected_weekdays
        except ValueError:
            print("星期格式错误，将查询所有星期")
            return list(range(1, 8))

    def _parse_sections(self, section: str) -> list:
        """解析节次"""
        try:
            selected_sections = [int(x.strip()) for x in section.split(',')]
            selected_sections = [x for x in selected_sections if 1 <= x <= 7]
            if not selected_sections:
                selected_sections = list(range(1, 8))  # 默认所有节次
            return selected_sections
        except ValueError:
            print("节次格式错误，将查询所有节次")
            return list(range(1, 8))

    def _try_get_from_cache(self, building_code: str, week: int, day_of_week: str, section: str,
                            room_number: str, selected_weekdays: list, selected_sections: list) -> Optional[str]:
        """尝试从缓存获取数据"""
        # 定期清理过期缓存
        if self.cache_manager.should_clean_cache():
            self.cache_manager.clean_expired_cache()

        # 使用新的缓存键策略：教学楼+周次
        cache_key = self.cache_manager.generate_cache_key(building_code, str(week))
        cached_data = self.cache_manager.get(cache_key)

        if cached_data:
            # print(f"[DEBUG] 使用缓存数据，避免重复网络请求")

            # 从缓存的原始数据重新分析
            rooms_info = cached_data["rooms_info"]
            analysis_result = RoomAnalyzer.analyze_room_availability(
                rooms_info, selected_weekdays, selected_sections, room_number
            )

            # 智能处理未找到教室的情况
            if room_number and not analysis_result['target_room_info']:
                # 尝试智能匹配相似教室
                suggested_room = RoomAnalyzer.find_similar_room(room_number, analysis_result['all_rooms'])
                if suggested_room:
                    # print(f"[DEBUG] 缓存中未找到精确匹配教室 {room_number}，但找到相似教室 {suggested_room}，自动查询")
                    # 使用建议的教室重新查询
                    analysis_result = RoomAnalyzer.analyze_room_availability(
                        rooms_info, selected_weekdays, selected_sections, suggested_room
                    )
                    # 修改结果说明
                    original_room = room_number
                    room_number = suggested_room

            # 格式化结果
            result = self._format_result(
                analysis_result, building_code, room_number, selected_weekdays, selected_sections
            )

            # 添加缓存标记
            if room_number:
                # print(f"[DEBUG] 从缓存中生成特定教室查询结果")
                cache_result = result
            else:
                # print(f"[DEBUG] 从缓存中生成普通教室查询结果")
                cache_result = result

            # 如果是智能匹配的结果，添加说明
            if 'original_room' in locals() and original_room != room_number:
                cache_result = f"未找到 {original_room}，已查询 {room_number}:\n{cache_result}"

            return cache_result

        return None

    def _extract_room_number_from_messages(self, messages) -> Optional[str]:
        """从对话历史中提取房间号"""
        try:
            for msg in messages:
                if hasattr(msg, 'content') and msg.content:
                    content = msg.content.lower()
                    # 查找各种教室编号格式
                    patterns = [
                        r'(?:思源|思西|思东|九教|八教|五教|二教|逸夫|机械)(\d+)',
                        r'(sx\d+)',  # SX开头的编号
                        r'(\d{3})'  # 三位数字编号
                    ]
                    for pattern in patterns:
                        matches = re.findall(pattern, content)
                        if matches:
                            # 提取最后一个匹配的教室编号
                            room_number = matches[-1].upper()
                            # 如果没有SX前缀，添加它
                            if not room_number.startswith('SX') and room_number.isdigit():
                                room_number = f"SX{room_number}"
                            print(f"[DEBUG] 从用户输入中提取到教室编号: {room_number}")
                            return room_number
                    break
        except Exception as e:
            print(f"[DEBUG] 从消息中提取房间号失败: {e}")
        return None

    def _build_query_params(self, building_code: str, week: int) -> dict:
        """构建查询参数"""
        return {
            'zxjxjhh': '2025-2026-1-2',
            'zc': str(week),
            'jxlh': building_code,
            'jash': '',
            'submit': '+%E6%9F%A5+%E8%AF%A2+',
            'has_advance_query': ''
        }

    def _format_result(self, analysis_result: dict, building_code: str, room_number: str,
                       selected_weekdays: list, selected_sections: list) -> str:
        """格式化结果"""
        if analysis_result['is_target_room']:
            # 特定教室查询结果
            return ResultFormatter.format_specific_room_result(
                analysis_result, building_code, room_number, selected_weekdays, selected_sections
            )
        else:
            # 普通教室查询结果
            return ResultFormatter.format_general_result(
                analysis_result, building_code, selected_weekdays, selected_sections
            )

    def _update_cache(self, building_code: str, week: int, day_of_week: str, section: str,
                      rooms_info: list, all_rooms: list) -> None:
        """更新缓存 - 使用新的缓存策略"""
        try:
            # 使用新的缓存键策略：教学楼+周次
            cache_key = self.cache_manager.generate_cache_key(building_code, str(week))
            self.cache_manager.set(cache_key, rooms_info, all_rooms, building_code)
        except Exception as e:
            print(f"[DEBUG] 缓存更新失败: {e}")

    def _generate_no_room_result(self, room_number: str, building_code: str, all_rooms: list) -> str:
        """生成未找到教室的结果"""
        building_name = BuildingMapper.get_building_name(building_code)
        result = f"**{room_number} 查询结果** ({building_name})\n\n"
        result += f"❌ 未找到教室 {room_number}，请检查教室编号是否正确\n\n"

        # 提供现有教室列表作为参考
        if all_rooms:
            result += f"📍 **{building_name}现有教室包括**:\n"
            # 显示前10个教室作为示例
            sample_rooms = sorted(all_rooms)[:10]
            for room in sample_rooms:
                result += f"  • {room}\n"
            if len(all_rooms) > 10:
                result += f"  ... 共{len(all_rooms)}个教室\n"
            result += f"\n💡 建议：请从上述教室中选择正确的编号重新查询\n"

        return result


# 辅助工具：获取当前学期信息
class CurrentTimeInfoTool(BaseTool):
    """获取当前时间信息工具"""

    name: str = "get_time"
    description: str = "获取当前周次、星期、节次"

    def _run(self, run_manager: Optional[CallbackManagerForToolRun] = None) -> str:
        """获取当前时间信息"""
        try:
            # 获取当前时间
            now = datetime.datetime.now()

            # 基于当前第8周计算其他周次
            # 2025年11月8日是周六，第8周应从2025年11月3日(周一)开始
            week8_start_date = datetime.date(2025, 11, 3)  # 第8周周一

            # 计算与第8周周一的天数差
            current_date = now.date()
            days_diff = (current_date - week8_start_date).days
            week_diff = days_diff // 7

            week_number = 8 + week_diff  # 基于第8周计算

            # 获取星期几 (1-7, 周一到周日)
            day_of_week = now.weekday() + 1

            # 简单估算节次 (实际应该根据课程时间表)
            hour = now.hour
            if 8 <= hour < 10:
                section = 1
            elif 10 <= hour < 12:
                section = 2
            elif 12 <= hour < 14:
                section = 3
            elif 14 <= hour < 16:
                section = 4
            elif 16 <= hour < 18:
                section = 5
            elif 19 <= hour < 21:
                section = 6
            elif 21 <= hour < 22:
                section = 7
            else:
                # 非上课时间段：18:00-19:00(晚餐), 其他时间
                section = None

            # 极简返回格式
            if section is None:
                result = f"第{week_number}周 周{day_of_week} 非上课时间"
            else:
                result = f"第{week_number}周 周{day_of_week} 第{section}节"

            return result

        except Exception as e:
            error_msg = f"获取时间信息时发生错误: {str(e)}"
            print(f"[CurrentTimeInfoTool] 错误: {error_msg}")
            return error_msg


if __name__ == "__main__":
    # 测试代码
    print("重构后的教室查询工具测试")
    print("=" * 30)

    # 测试时间信息工具
    time_tool = CurrentTimeInfoTool()
    time_info = time_tool._run()
    print(f"时间信息: {time_info}")
